#define VERBOSE
#ifdef VERBOSE
#define IF_VERBOSE(x) ({x;})
#else
#define IF_VERBOSE(x)
#endif



#include <SPI.h>
#include <RF24.h>
#include <RF24Network.h>
#include "S_message.h"
#include <printf.h>
#include <Ethernet.h>
#include <sha256.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <MsTimer2.h>



const uint8_t led_indicator_pin = 7;         //upload indicator
const uint8_t sd_ss_pin = 4;         //SD chip select


//sense net name
#define SENSE_NET_NAME "BEL"

//pins for radio
const uint8_t rf_ce = 9;
const uint8_t rf_csn = 6;

//radio channel
const uint8_t radio_channel = 92;

// Data wire is plugged into pin 2 on the Arduino
const uint8_t one_wire_pin=2;
const uint8_t temperature_precision=9;

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(one_wire_pin);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature tempSensors(&oneWire);
uint8_t numberOfDevices;
DeviceAddress tempDeviceAddress;

#define MAX_NUMBER_OF_SENSORS 10
float lastTempSamples[MAX_NUMBER_OF_SENSORS] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};

//if the delta between old and new sensor reading is higher than 1, then we will upload
float deltaT = 1; 

char tempSensorName[] = "TEMP-xxxx------------";
char powrSensorName[] = "POWR-xxxx";
char battSensorName[] = "BATT-xxxx";

//temp sensors will be scanned every 5 seconds
long unsigned sensor_scan_timer = 0;
const long unsigned sensor_scan_timer_timeout = 5000; 

//temperature readings will be uploaded at leasts once every 5000 cycles
const uint16_t forceReportReset = 5000;
uint16_t forceReport = forceReportReset;



RF24 radio (rf_ce, rf_csn);
RF24Network network (radio);
RF24NetworkHeader header;
S_message message;
const uint16_t my_address = 0; //base unit


EthernetClient ethClient;  //Ethernet shield attached to pins 10, 11, 12, 13
byte mac[] = {0x00, 0xB5, 0x89, 0xC8, 0x76, 0x74 };



void setup(){
  IF_VERBOSE(Serial.begin(57600));
  IF_VERBOSE(printf_begin());
  
  pinMode(led_indicator_pin, OUTPUT);
  MsTimer2::set(500, flash);
  MsTimer2::start();
  
  pinMode(sd_ss_pin, OUTPUT);
  digitalWrite(sd_ss_pin, HIGH); //switch off the SD module  
  
  radio.begin();
  network.begin (radio_channel, my_address);
  
  tempSensors.begin();
  numberOfDevices = tempSensors.getDeviceCount();
  if (numberOfDevices > MAX_NUMBER_OF_SENSORS)
    numberOfDevices = MAX_NUMBER_OF_SENSORS;
  
  IF_VERBOSE(printf("Found %u sensors\n", numberOfDevices));

  for(uint8_t i=0; i<numberOfDevices; i++){
    if (tempSensors.getAddress(tempDeviceAddress, i)) {
      tempSensors.setResolution(tempDeviceAddress, temperature_precision);
    }
    else{
      IF_VERBOSE(printf("ghost sensor at %u\n",i));
    }
  }  
  
  if (Ethernet.begin(mac) == 0){
    IF_VERBOSE(printf("DHCP fail. Press RESET.\n"));
    MsTimer2::stop();
    digitalWrite(led_indicator_pin, LOW);
    while (1);
  }

    MsTimer2::stop();
    digitalWrite(led_indicator_pin, HIGH);  
    IF_VERBOSE(Serial.println(Ethernet.localIP()));
  
}

void loop(){
  
  network.update(); //pump radio network reguarly
  
  while (network.available() ){
    network.read(header,&message,sizeof(message));
    if (header.type == 'S'){
      if (bitRead(message.mask, S_MASK_POWER)){
        Hex2Char((uint8_t*)&header.from_node, powrSensorName, 5, 2);
        upload(message.power_reading, SENSE_NET_NAME, powrSensorName);
      }
      
      if (bitRead(message.mask, S_MASK_BATTERY)){
        Hex2Char((uint8_t*)&header.from_node, battSensorName, 5, 2);
        upload(message.battery_reading, SENSE_NET_NAME, battSensorName);
      }
      
      if (bitRead(message.mask, S_MASK_TEMP)){
        Hex2Char((uint8_t*)&header.from_node, tempSensorName, 5, 2);
        tempSensorName[9]=0;
        upload(message.temp_reading, SENSE_NET_NAME, tempSensorName);
      }

    }
  }
  
  
  if (millis() > sensor_scan_timer){
    sensor_scan_timer = millis()+sensor_scan_timer_timeout;
    forceReport--;
    readSensors(!forceReport);
    if (!forceReport) 
      forceReport = forceReportReset;

  }
  
}


void readSensors(boolean forceUpload){

  tempSensors.requestTemperatures();
  
  for (uint8_t i = 0; i<numberOfDevices; i++){
    
    if (tempSensors.getAddress(tempDeviceAddress, i)){

      float t = tempSensors.getTempC(tempDeviceAddress);
      
      if (t>=lastTempSamples[i]+deltaT || 
          t<=lastTempSamples[i]-deltaT ||
          forceUpload)
      {
        lastTempSamples[i] = t;
        Hex2Char(tempDeviceAddress, tempSensorName, 5, 8);
        upload(t, SENSE_NET_NAME, tempSensorName);
      }
    }
  }
}

void Hex2Char(uint8_t* hex, char* ch, int pos, int len){

    for (uint8_t i=0; i<len; i++) {
      ch[pos+2*i]="0123456789abcdef"[hex[i]>>4];
      ch[pos+2*i+1]="0123456789abcdef"[hex[i]&0xf];
    }
}

void upload(int val, char* senseNetName, char* sensorName){
  
  IF_VERBOSE(printf("Uploading %s:%s->%d...", senseNetName, sensorName, val));
  
  
  const char serverName[] = "belianovsoftsensenet.appspot.com";
  const char key[] = "qwertyu1976";
  char hashmac[65] = "----------------------------------------------------------------";
  
  
  blinkUploadIndicator();

  
  //compute hash value
  Sha256.initHmac((uint8_t*)key,sizeof (key) - 1);
  Sha256.print(senseNetName);Sha256.print(sensorName);Sha256.print(val);
  Hex2Char(Sha256.resultHmac(), hashmac, 0, 32);
  
  if (ethClient.connect(serverName, 80)){
    
    
    digitalWrite(led_indicator_pin, HIGH);

    // Make a HTTP request:;
    ethClient.print("GET /sensorvalueloader?SenseNetName="); ethClient.print(senseNetName);
    ethClient.print("&SensorName="); ethClient.print(sensorName);
    ethClient.print("&SensorValue="); ethClient.print(val);
    ethClient.print("&hmac="); ethClient.print(hashmac); ethClient.println(" HTTP/1.1");
    ethClient.print("Host: "); ethClient.println(serverName);
    ethClient.println();
    IF_VERBOSE(printf("done\n"));
  }
  else {
    IF_VERBOSE(printf("fail\n"));
    digitalWrite(led_indicator_pin, LOW);
  }
  
  ethClient.stop();  
}

void flash(){

  static boolean output = HIGH;  
  digitalWrite(led_indicator_pin, output);  
  output = !output;

//  digitalWrite(INDICATOR, !digitalRead(INDICATOR));
}

void blinkUploadIndicator(){
  digitalWrite(led_indicator_pin, LOW);
  delay(15);
  digitalWrite(led_indicator_pin, HIGH);
}

